package com.gitee.cashzhang27.test.boot.functional.configuration;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.gitee.cashzhang27.test.boot.functional.properties.RedissonClusterServerProperties;
import com.gitee.cashzhang27.test.boot.functional.properties.RedissonMasterSlaveServerProperties;
import com.gitee.cashzhang27.test.boot.functional.properties.RedissonProperties;
import com.gitee.cashzhang27.test.boot.functional.properties.RedissonReplicatedServerProperties;
import com.gitee.cashzhang27.test.boot.functional.properties.RedissonSentinelServerProperties;
import com.gitee.cashzhang27.test.boot.functional.properties.RedissonSingleServerProperties;
import java.lang.reflect.Method;
import java.time.Duration;
import lombok.AllArgsConstructor;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;

/**
 * This is a Redis configuration.
 *
 * @author Cash Zhang
 * @version v1.0
 * @since 2020/04/09 21:54
 */
@EnableCaching
@Configuration
@AllArgsConstructor
@ConditionalOnClass({Redisson.class, RedisOperations.class})
@AutoConfigureBefore(RedisAutoConfiguration.class)
@EnableConfigurationProperties({RedissonProperties.class, RedisProperties.class})
public class RedisConfiguration {

  private final ConfigurableEnvironment configurableEnvironment;

  /**
   * redissonClient Bean.
   *
   * @param redisProperties    redis属性
   * @param redissonProperties redisson属性
   * @return redissonClient
   */
  @Bean(destroyMethod = "shutdown")
  public RedissonClient redissonClient(RedisProperties redisProperties,
                                       RedissonProperties redissonProperties) {
    Config config = new Config();

    // common properties
    config.setThreads(redissonProperties.getThreads());
    config.setNettyThreads(redissonProperties.getNettyThreads());
    config.setCodec(new JsonJacksonCodec(this.objectMapper()));
    config.setTransportMode(redissonProperties.getTransportMode());
    config.setLockWatchdogTimeout(redissonProperties.getLockWatchdogTimeout());
    config.setKeepPubSubOrder(redissonProperties.isKeepPubSubOrder());
    config.setMinCleanUpDelay(redissonProperties.getMinCleanUpDelay());
    config.setMaxCleanUpDelay(redissonProperties.getMaxCleanUpDelay());
    config.setCleanUpKeysAmount(redissonProperties.getCleanUpKeysAmount());

    if (redissonProperties.getSentinel().isEnabled()) {
      // 哨兵模式
      this.buildRedissonSentinelConfig(redisProperties, redissonProperties, config);
    } else if (redissonProperties.getCluster().isEnabled()) {
      // 集群模式
      this.buildRedissonClusterConfig(redisProperties, redissonProperties, config);
    } else if (redissonProperties.getReplicated().isEnabled()) {
      // 云托管模式
      this.buildRedissonReplicatedConfig(redisProperties, redissonProperties, config);
    } else if (redissonProperties.getMasterSlave().isEnabled()) {
      // 主从模式
      this.buildRedissonMasterSlaveConfig(redisProperties, redissonProperties, config);
    } else {
      // 单节点模式
      this.buildRedissonSingleConfig(redisProperties, redissonProperties, config);
    }

    return Redisson.create(config);
  }


  /**
   * 构建 Redisson 哨兵模式配置.
   *
   * @param redisProperties    redis属性
   * @param redissonProperties redisson属性
   * @param config             redisson配置
   */
  private void buildRedissonSentinelConfig(RedisProperties redisProperties,
                                           RedissonProperties redissonProperties, Config config) {
    RedisProperties.Sentinel sentinel = redisProperties.getSentinel();
    RedissonSentinelServerProperties redissonSentinel = redissonProperties.getSentinel();

    config.useSentinelServers()
        // base properties
        .setIdleConnectionTimeout(redissonSentinel.getIdleConnectionTimeout())
        .setSubscriptionsPerConnection(redissonSentinel.getSubscriptionsPerConnection())
        .setRetryAttempts(redissonSentinel.getRetryAttempts())
        .setRetryInterval(redissonSentinel.getRetryInterval())
        .setTimeout(redissonSentinel.getTimeout())
        .setClientName(redissonSentinel.getClientName())
        .setConnectTimeout(Long.valueOf(redisProperties.getTimeout().toMillis()).intValue())
        .setSslEnableEndpointIdentification(redissonSentinel.isSslEnableEndpointIdentification())
        .setSslProvider(redissonSentinel.getSslProvider())
        .setSslTruststore(redissonSentinel.getSslTruststore())
        .setSslTruststorePassword(redissonSentinel.getSslTruststorePassword())
        .setSslKeystore(redissonSentinel.getSslKeystore())
        .setSslKeystorePassword(redissonSentinel.getSslKeystorePassword())
        .setPingConnectionInterval(redissonSentinel.getPingConnectionInterval())
        .setKeepAlive(redissonSentinel.isKeepAlive())
        .setTcpNoDelay(redissonSentinel.isTcpNoDelay())
        // sentinel properties
        .addSentinelAddress(sentinel.getNodes().toArray(new String[0]))
        .setScanInterval(redissonSentinel.getScanInterval())
        .setCheckSentinelsList(redissonSentinel.isCheckSentinelsList())
        .setMasterName(sentinel.getMaster())
        .setDatabase(redisProperties.getDatabase())
        // base master slave properties
        .setLoadBalancer(redissonSentinel.getLoadBalancer())
        .setSlaveConnectionMinimumIdleSize(redissonSentinel.getSlaveConnectionMinimumIdleSize())
        .setSlaveConnectionPoolSize(redissonSentinel.getSlaveConnectionPoolSize())
        .setFailedSlaveReconnectionInterval(redissonSentinel.getFailedSlaveReconnectionInterval())
        .setFailedSlaveCheckInterval(redissonSentinel.getFailedSlaveCheckInterval())
        .setMasterConnectionMinimumIdleSize(redissonSentinel.getMasterConnectionMinimumIdleSize())
        .setMasterConnectionPoolSize(redissonSentinel.getMasterConnectionPoolSize())
        .setReadMode(redissonSentinel.getReadMode())
        .setSubscriptionMode(redissonSentinel.getSubscriptionMode())
        .setSubscriptionConnectionMinimumIdleSize(
            redissonSentinel.getSubscriptionConnectionMinimumIdleSize())
        .setSubscriptionConnectionPoolSize(redissonSentinel.getSubscriptionConnectionPoolSize())
        .setDnsMonitoringInterval(redissonSentinel.getDnsMonitoringInterval());
  }


  /**
   * 构建 Redisson 集群模式配置.
   *
   * @param redisProperties    redis属性
   * @param redissonProperties redisson属性
   * @param config             redisson配置
   */
  private void buildRedissonClusterConfig(RedisProperties redisProperties,
                                          RedissonProperties redissonProperties, Config config) {
    RedisProperties.Cluster cluster = redisProperties.getCluster();
    RedissonClusterServerProperties redissonCluster = redissonProperties.getCluster();

    config.useClusterServers()
        // base properties
        .setIdleConnectionTimeout(redissonCluster.getIdleConnectionTimeout())
        .setSubscriptionsPerConnection(redissonCluster.getSubscriptionsPerConnection())
        .setRetryAttempts(redissonCluster.getRetryAttempts())
        .setRetryInterval(redissonCluster.getRetryInterval())
        .setTimeout(redissonCluster.getTimeout())
        .setClientName(redissonCluster.getClientName())
        .setConnectTimeout(Long.valueOf(redisProperties.getTimeout().toMillis()).intValue())
        .setSslEnableEndpointIdentification(redissonCluster.isSslEnableEndpointIdentification())
        .setSslProvider(redissonCluster.getSslProvider())
        .setSslTruststore(redissonCluster.getSslTruststore())
        .setSslTruststorePassword(redissonCluster.getSslTruststorePassword())
        .setSslKeystore(redissonCluster.getSslKeystore())
        .setSslKeystorePassword(redissonCluster.getSslKeystorePassword())
        .setPingConnectionInterval(redissonCluster.getPingConnectionInterval())
        .setKeepAlive(redissonCluster.isKeepAlive())
        .setTcpNoDelay(redissonCluster.isTcpNoDelay())
        // cluster properties
        .addNodeAddress(cluster.getNodes().toArray(new String[0]))
        .setScanInterval(redissonCluster.getScanInterval())
        .setCheckSlotsCoverage(redissonCluster.isCheckSlotsCoverage())
        // base master slave properties
        .setLoadBalancer(redissonCluster.getLoadBalancer())
        .setSlaveConnectionMinimumIdleSize(redissonCluster.getSlaveConnectionMinimumIdleSize())
        .setSlaveConnectionPoolSize(redissonCluster.getSlaveConnectionPoolSize())
        .setFailedSlaveReconnectionInterval(redissonCluster.getFailedSlaveReconnectionInterval())
        .setFailedSlaveCheckInterval(redissonCluster.getFailedSlaveCheckInterval())
        .setMasterConnectionMinimumIdleSize(redissonCluster.getMasterConnectionMinimumIdleSize())
        .setMasterConnectionPoolSize(redissonCluster.getMasterConnectionPoolSize())
        .setReadMode(redissonCluster.getReadMode())
        .setSubscriptionMode(redissonCluster.getSubscriptionMode())
        .setSubscriptionConnectionMinimumIdleSize(
            redissonCluster.getSubscriptionConnectionMinimumIdleSize())
        .setSubscriptionConnectionPoolSize(redissonCluster.getSubscriptionConnectionPoolSize())
        .setDnsMonitoringInterval(redissonCluster.getDnsMonitoringInterval());
  }

  /**
   * 构建 Redisson 云托管模式配置.
   *
   * @param redisProperties    redis属性
   * @param redissonProperties redisson属性
   * @param config             redisson配置
   */
  private void buildRedissonReplicatedConfig(RedisProperties redisProperties,
                                             RedissonProperties redissonProperties, Config config) {
    RedissonReplicatedServerProperties redissonReplicated = redissonProperties.getReplicated();

    config.useReplicatedServers()
        // base properties
        .setIdleConnectionTimeout(redissonReplicated.getIdleConnectionTimeout())
        .setSubscriptionsPerConnection(redissonReplicated.getSubscriptionsPerConnection())
        .setRetryAttempts(redissonReplicated.getRetryAttempts())
        .setRetryInterval(redissonReplicated.getRetryInterval())
        .setTimeout(redissonReplicated.getTimeout())
        .setClientName(redissonReplicated.getClientName())
        .setConnectTimeout(Long.valueOf(redisProperties.getTimeout().toMillis()).intValue())
        .setSslEnableEndpointIdentification(redissonReplicated.isSslEnableEndpointIdentification())
        .setSslProvider(redissonReplicated.getSslProvider())
        .setSslTruststore(redissonReplicated.getSslTruststore())
        .setSslTruststorePassword(redissonReplicated.getSslTruststorePassword())
        .setSslKeystore(redissonReplicated.getSslKeystore())
        .setSslKeystorePassword(redissonReplicated.getSslKeystorePassword())
        .setPingConnectionInterval(redissonReplicated.getPingConnectionInterval())
        .setKeepAlive(redissonReplicated.isKeepAlive())
        .setTcpNoDelay(redissonReplicated.isTcpNoDelay())
        // replicated properties
        .addNodeAddress(redissonReplicated.getNodes().toArray(new String[0]))
        .setDatabase(redisProperties.getDatabase())
        .setScanInterval(redissonReplicated.getScanInterval())
        // base master slave properties
        .setLoadBalancer(redissonReplicated.getLoadBalancer())
        .setSlaveConnectionMinimumIdleSize(redissonReplicated.getSlaveConnectionMinimumIdleSize())
        .setSlaveConnectionPoolSize(redissonReplicated.getSlaveConnectionPoolSize())
        .setFailedSlaveReconnectionInterval(
            redissonReplicated.getFailedSlaveReconnectionInterval())
        .setFailedSlaveCheckInterval(redissonReplicated.getFailedSlaveCheckInterval())
        .setMasterConnectionMinimumIdleSize(
            redissonReplicated.getMasterConnectionMinimumIdleSize())
        .setMasterConnectionPoolSize(redissonReplicated.getMasterConnectionPoolSize())
        .setReadMode(redissonReplicated.getReadMode())
        .setSubscriptionMode(redissonReplicated.getSubscriptionMode())
        .setSubscriptionConnectionMinimumIdleSize(
            redissonReplicated.getSubscriptionConnectionMinimumIdleSize())
        .setSubscriptionConnectionPoolSize(redissonReplicated.getSubscriptionConnectionPoolSize())
        .setDnsMonitoringInterval(redissonReplicated.getDnsMonitoringInterval());
  }


  /**
   * 构建 Redisson 主从模式配置.
   *
   * @param redisProperties    redis属性
   * @param redissonProperties redisson属性
   * @param config             redisson配置
   */
  private void buildRedissonMasterSlaveConfig(RedisProperties redisProperties,
                                              RedissonProperties redissonProperties,
                                              Config config) {
    RedissonMasterSlaveServerProperties redissonMasterSlave = redissonProperties.getMasterSlave();

    config.useMasterSlaveServers()
        // base properties
        .setIdleConnectionTimeout(redissonMasterSlave.getIdleConnectionTimeout())
        .setSubscriptionsPerConnection(redissonMasterSlave.getSubscriptionsPerConnection())
        .setRetryAttempts(redissonMasterSlave.getRetryAttempts())
        .setRetryInterval(redissonMasterSlave.getRetryInterval())
        .setTimeout(redissonMasterSlave.getTimeout())
        .setClientName(redissonMasterSlave.getClientName())
        .setConnectTimeout(Long.valueOf(redisProperties.getTimeout().toMillis()).intValue())
        .setSslEnableEndpointIdentification(redissonMasterSlave.isSslEnableEndpointIdentification())
        .setSslProvider(redissonMasterSlave.getSslProvider())
        .setSslTruststore(redissonMasterSlave.getSslTruststore())
        .setSslTruststorePassword(redissonMasterSlave.getSslTruststorePassword())
        .setSslKeystore(redissonMasterSlave.getSslKeystore())
        .setSslKeystorePassword(redissonMasterSlave.getSslKeystorePassword())
        .setPingConnectionInterval(redissonMasterSlave.getPingConnectionInterval())
        .setKeepAlive(redissonMasterSlave.isKeepAlive())
        .setTcpNoDelay(redissonMasterSlave.isTcpNoDelay())
        // replicated properties
        .setDatabase(redisProperties.getDatabase())
        .addSlaveAddress(redissonMasterSlave.getSlaveAddresses().toArray(new String[0]))
        .setMasterAddress(redissonMasterSlave.getMasterAddress())
        // base master slave properties
        .setLoadBalancer(redissonMasterSlave.getLoadBalancer())
        .setSlaveConnectionMinimumIdleSize(
            redissonMasterSlave.getSlaveConnectionMinimumIdleSize())
        .setSlaveConnectionPoolSize(redissonMasterSlave.getSlaveConnectionPoolSize())
        .setFailedSlaveReconnectionInterval(
            redissonMasterSlave.getFailedSlaveReconnectionInterval())
        .setFailedSlaveCheckInterval(redissonMasterSlave.getFailedSlaveCheckInterval())
        .setMasterConnectionMinimumIdleSize(
            redissonMasterSlave.getMasterConnectionMinimumIdleSize())
        .setMasterConnectionPoolSize(redissonMasterSlave.getMasterConnectionPoolSize())
        .setReadMode(redissonMasterSlave.getReadMode())
        .setSubscriptionMode(redissonMasterSlave.getSubscriptionMode())
        .setSubscriptionConnectionMinimumIdleSize(
            redissonMasterSlave.getSubscriptionConnectionMinimumIdleSize())
        .setSubscriptionConnectionPoolSize(
            redissonMasterSlave.getSubscriptionConnectionPoolSize())
        .setDnsMonitoringInterval(redissonMasterSlave.getDnsMonitoringInterval());
  }

  /**
   * 构建 Redisson 单节点模式配置.
   *
   * @param redisProperties    redis属性
   * @param redissonProperties redisson属性
   * @param config             redisson配置
   */
  private void buildRedissonSingleConfig(RedisProperties redisProperties,
                                         RedissonProperties redissonProperties, Config config) {
    RedissonSingleServerProperties redissonSingle = redissonProperties.getSingle();
    String prefix = "redis://";
    if (redisProperties.isSsl()) {
      prefix = "rediss://";
    }

    config
        .useSingleServer()
        // base properties
        .setIdleConnectionTimeout(redissonSingle.getIdleConnectionTimeout())
        .setSubscriptionsPerConnection(redissonSingle.getSubscriptionsPerConnection())
        .setRetryAttempts(redissonSingle.getRetryAttempts())
        .setRetryInterval(redissonSingle.getRetryInterval())
        .setTimeout(redissonSingle.getTimeout())
        .setClientName(redissonSingle.getClientName())
        .setConnectTimeout(Long.valueOf(redisProperties.getTimeout().toMillis()).intValue())
        .setSslEnableEndpointIdentification(redissonSingle.isSslEnableEndpointIdentification())
        .setSslProvider(redissonSingle.getSslProvider())
        .setSslTruststore(redissonSingle.getSslTruststore())
        .setSslTruststorePassword(redissonSingle.getSslTruststorePassword())
        .setSslKeystore(redissonSingle.getSslKeystore())
        .setSslKeystorePassword(redissonSingle.getSslKeystorePassword())
        .setPingConnectionInterval(redissonSingle.getPingConnectionInterval())
        .setKeepAlive(redissonSingle.isKeepAlive())
        .setTcpNoDelay(redissonSingle.isTcpNoDelay())
        // standalone properties
        .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
        .setDatabase(redisProperties.getDatabase())
        .setPassword(redisProperties.getPassword())
        .setSubscriptionConnectionMinimumIdleSize(
            redissonSingle.getSubscriptionConnectionMinimumIdleSize())
        .setSubscriptionConnectionPoolSize(redissonSingle.getSubscriptionConnectionPoolSize())
        .setConnectionMinimumIdleSize(redissonSingle.getConnectionMinimumIdleSize())
        .setConnectionPoolSize(redissonSingle.getConnectionPoolSize())
        .setDnsMonitoringInterval(redissonSingle.getDnsMonitoringInterval());
  }

  /**
   * redisson connection factory.
   *
   * @param redissonClient redissonClient
   * @return LettuceConnectionFactory
   */
  @Bean
  public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redissonClient) {
    return new RedissonConnectionFactory(redissonClient);
  }

  /**
   * redis template.
   *
   * @param redisConnectionFactory LettuceConnectionFactory
   * @return RedisTemplate
   */
  @Bean
  public RedisTemplate<String, Object> redisTemplate(
      RedissonConnectionFactory redisConnectionFactory) {
    Jackson2JsonRedisSerializer<Object> serializer =
        new Jackson2JsonRedisSerializer<>(Object.class);

    serializer.setObjectMapper(this.objectMapper());

    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(serializer);
    redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    redisTemplate.setHashValueSerializer(serializer);
    redisTemplate.afterPropertiesSet();
    // explicitly enable transaction support
    redisTemplate.setEnableTransactionSupport(true);
    return redisTemplate;
  }

  /**
   * redis template.
   *
   * @param redisConnectionFactory LettuceConnectionFactory
   * @return RedisTemplate
   */
  @Bean
  public StringRedisTemplate stringRedisTemplate(RedissonConnectionFactory redisConnectionFactory) {
    Jackson2JsonRedisSerializer<String> serializer =
        new Jackson2JsonRedisSerializer<>(String.class);

    serializer.setObjectMapper(this.objectMapper());

    StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
    stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
    stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
    stringRedisTemplate.setValueSerializer(serializer);
    stringRedisTemplate.setHashKeySerializer(new StringRedisSerializer());
    stringRedisTemplate.setHashValueSerializer(serializer);
    stringRedisTemplate.afterPropertiesSet();
    // explicitly enable transaction support
    stringRedisTemplate.setEnableTransactionSupport(true);
    return stringRedisTemplate;
  }


  /**
   * 序列Redis所需ObjectMapper.
   *
   * @return ObjectMapper
   */
  private ObjectMapper objectMapper() {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    objectMapper.configure(MapperFeature.USE_ANNOTATIONS, false);
    // 反序列化时，忽略目标对象没有的属性
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    // 此项必须配置，否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
    objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(),
        ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_OBJECT);
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    return objectMapper;
  }

  /**
   * redis cache key generator.
   *
   * @return KeyGenerator
   */
  @Bean
  public KeyGenerator keyGenerator() {
    return new KeyGenerator() {
      @Override
      @NonNull
      public Object generate(
          @NonNull Object target, @NonNull Method method, @NonNull Object... params) {
        StringBuilder sb = new StringBuilder();
        sb.append(target.getClass().getName());
        sb.append(method.getName());
        for (Object obj : params) {
          sb.append(obj.toString());
        }
        return sb.toString();
      }
    };
  }

  /**
   * redis cache manager.
   *
   * @param redisTemplate          RedisTemplate
   * @param redisConnectionFactory LettuceConnectionFactory
   * @return RedisCacheManager
   */
  @Bean
  public RedisCacheManager redisCacheManager(
      RedisTemplate<String, Object> redisTemplate,
      RedissonConnectionFactory redisConnectionFactory) {
    String applicationName = this.configurableEnvironment.getProperty("spring.application.name");
    assert StringUtils.hasText(applicationName);

    return RedisCacheManager.builder(redisConnectionFactory)
        .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair.fromSerializer(
                    redisTemplate.getValueSerializer()))
            // 设置缓存有效期一小时
            .entryTtl(Duration.ofHours(1))
            // 计算前缀
            .computePrefixWith(name -> applicationName + StrUtil.COLON + name + StrUtil.COLON))
        // 有锁写
        .cacheWriter(RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory))
        // 事务
        .transactionAware()
        .build();
  }

}
